#ifndef ___torrent_control_h___
#define ___torrent_control_h___

#include <boost/utility.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/function.hpp>

#include "backup_server_connection.h"
#include "dispatcher.h"

#include "libtorrent/bencode.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/alert.hpp"


/*
 * A struct containing information about the current download
 * progress for a torrent.
 * piece_src: 0 = torrent, 1 = backup, 2 = multicast, the rest is undefined.
 */
struct progress_info 
{
    std::vector<int> priorities;
    libtorrent::bitfield downloaded;
    std::vector<libtorrent::partial_piece_info> piece_states; //This is filled by download_queue, may be empty.
    std::vector<int> piece_src;

};

/**
 * A struct containing information about current download info.
 */
struct download_info
{
    int num_peers;
    int num_unchoked;
    int num_connections;
    double download_rate; //In bytes.
    double upload_rate; //In bytes.
    size_t total_download; //In bytes. //FIXME: add_pice does not add total dl.
};

/**
 * The torrent_control controls one torrent download, and allows for
 * retrieving and setting priorities for individual pieces in a
 * torrent.
 */
class torrent_control : public boost::noncopyable
{
    public:
    /**
     * Creates a torrent control.
     * @param torrent_name Name of the .torrent-file, including path.
     * @param save_path Path to save the downloaded file.
     * @param port_range Port range for the BitTorrent connection.
     */
    torrent_control(std::string torrent_name,
                    std::string save_path,
                    std::pair<int,int> port_range,
                    bool auto_buffer,
                    std::string adress,
                    std::string port,
                    std::string file_name,
                    size_t max_simultaneous_downloads);
    /**
     * @ret Returns a progress_info containing information about the current download.
     */
    progress_info get_progress();

    /**
     * @ret Returns a download_info containing information about the current download.
     */
    download_info get_download_info();
    
    /**
     * @ret Number of pieces in the torrent-file.
     */
    int num_pieces() const { return torrent_handle.get_torrent_info().num_pieces(); }

    /**
     * @ret The sha1 hash for a piece in the torrent-file.
     */
    libtorrent::sha1_hash const get_piece_hash(size_t index){ return torrent_handle.get_torrent_info().hash_for_piece(index);}

    /**
     * Sets the critical window for pieces to be downloaded. If the piece isn't expected
     * to be finished downloaded before the critical window, it will be requested from
     * the backup server connection.
     * @param seconds The critical window in seconds.
     */
    void set_critical_window(double seconds);
    
    /**
     * @param bytes_per_second The download rate limit in bytes/second.
     */
    void set_download_rate_limit(int bytes_per_second);
    
    /**
     * @param bytes_per_second The upload rate limit in bytes/second.
     */
    void set_upload_rate_limit(int bytes_per_second);
 
    /**
     * Reads @length number of bytes starting from @offset into @buffer.
     * @param offset The starting point in bytes from where to read data.
     * @param length The number of bytes to read.
     * @param buffer A buffer where the data is copied to.
     * @ret The number of bytes to copy.
     */
    int read_data(size_t offset, size_t length, char * buffer);

    /**
     * Prebuffers data by requesting it from the backup server.
     */
    void pre_buffer(size_t offset, size_t length);

    /**
     * Adds a piece to the torrent by requesting it from a backup server connection.
     */
    void add_pieces_from_backup_server(size_t piece_index,
                                       size_t count);
    
    /**
     * Sets the function to be called when pieces are downloaded.
     */
    void set_piece_finished_callback(boost::function<void(int)> fun) 
    {
        piece_finished_callback = fun;
    }

    bool is_seed() { return torrent_handle.is_seed(); }

    std::vector<int> piece_src;

    private:
    
    /**
     * This method runs when a piece has completed download from the
     * backup server.
     */
    void on_download_complete(backup_server_connection::request req,
                              std::vector<char*> buffers);
    
    void set_piece_src(size_t index, int src);

    /**
     * Pops and dispatches libtorrent alerts
     */
    void alerts_thread_worker();

    
    libtorrent::torrent_handle torrent_handle;
    libtorrent::session torrent_session;
    backup_server_connection connection;
    double critical_window;
    dispatcher disp;
    bool auto_buffer;
    boost::thread alerts_thread;

    boost::function<void(int)> piece_finished_callback;
    
};


#endif // ___torrent_control_h___
